package cn.lycode.framework.web.service;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.lycode.common.constant.Constants;
import cn.lycode.common.core.domain.entity.SysUser;
import cn.lycode.common.core.domain.model.user.BaseUserInfo;
import cn.lycode.common.core.domain.model.user.LoginUserInfo;
import cn.lycode.common.enums.ErrorEnum;
import cn.lycode.common.utils.SecurityUtils;
import cn.lycode.framework.web.domain.LoginVO;
import cn.lycode.system.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 登录校验方法
 *
 * @author lyCode
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class SysLoginService {

    private final SysUserService sysUserService;
    private final SysPermissionService sysPermissionService;

    /**
     * 更新Session中的 LoginUserInfo
     * @param userId 用户id
     */
    public void syncLoginUserInfo(Object userId) {
        List<String> tokens = StpUtil.getTokenValueListByLoginId(userId);
        if (tokens.isEmpty()) {
            return;
        }
        for (String token : tokens) {
            SaSession saSession = StpUtil.getTokenSessionByToken(token); // 根据token获取用户session
            // 1. 查询当前用户的最新用户权限信息
            SysUser user = sysUserService.getSysUserByUserId(Long.valueOf(userId.toString()));
            LoginUserInfo loginUserInfo = getLoginUserInfo(user);
            // 2. 更新redis信息
            saSession.set(Constants.LOGIN_USER_INFO, loginUserInfo);
            log.warn("登录用户信息变更, userId:{}, token:{}", userId, token);
        }
    }

    /**
     * 登录验证
     * @param username 用户名
     * @param password 密码
     * @param code     验证码
     * @param uuid     唯一标识
     * @return 结果
     */
    public LoginVO login(String username, String password, String code, String uuid) {
        validateCaptcha(username, code, uuid);
        loginPreCheck(username, password);

        SysUser user = sysUserService.getSysUserByUsernameOrNo(username);
        ErrorEnum.BAD_USERNAME_OR_PASSWORD.assertNotNull(user);
        //校验密码
        ErrorEnum.BAD_USERNAME_OR_PASSWORD.assertTrue(SecurityUtils.checkPassword(password, user.getPassword()));
        // 校验指定账号是否已被封禁，如果被封禁则抛出异常 `DisableServiceException`
        Long userId = user.getId();
        // 用户状态校验（禁用状态校验）
        validateUserStatus(user);
        // 通过校验后，再进行登录
        StpUtil.login(userId, createLoginModel());
        // 登录完成把用户放到session
        LoginUserInfo loginUserInfo = getLoginUserInfo(user);
        StpUtil.getTokenSession().set(Constants.LOGIN_USER_INFO, loginUserInfo);

        //构建登录接口返回内容
        LoginVO loginVo = new LoginVO();
        loginVo.setId(loginUserInfo.getBaseUserInfo().getId());
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setUserInfo(loginUserInfo.getBaseUserInfo());
        return loginVo;
    }

    private SaLoginModel createLoginModel() {
        SaLoginModel model = new SaLoginModel();
        model.setDevice("PC");
        //token固定超时
        model.setTimeout(604800);
        return model;
    }

    private void validateUserStatus(SysUser user) {
        StpUtil.checkDisable(user.getId());
    }

    @NotNull
    private LoginUserInfo getLoginUserInfo(SysUser user) {
        BaseUserInfo userInfo = new BaseUserInfo();
        BeanUtils.copyProperties(user, userInfo);
        LoginUserInfo LoginUserInfo = new LoginUserInfo();
        LoginUserInfo.setBaseUserInfo(userInfo);
        //如果需要存更多用户信息到session中，且不返回给前端，可以扩展LoginUserInfo
        return LoginUserInfo;
    }

    /**
     * 登录前置校验
     * @param username 用户名
     * @param password 用户密码
     */
    private void loginPreCheck(String username, String password) {
        // TODO 校验
        // 用户名或密码为空 错误
        // 密码如果不在指定范围内 错误
        // 用户名不在指定范围内 错误
        // IP黑名单校验
        // 校验不通过使用异步线程记录.
    }


    /**
     * 校验验证码
     * @param username 用户名
     * @param code     验证码
     * @param uuid     唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid) {

    }
}
